﻿Imports RADAR

Module Main

    Sub Main()
        StartUpServer()
        Console.WriteLine("Server started. Press ENTER to quit.")
        Console.ReadLine()
    End Sub

    Private secrets As NASAuthList
    Private radius1812 As RADIUSServer
    Private radius1645 As RADIUSServer

    Private Sub StartUpServer()

        ' First, let's load a list of RADIUS shared secrets
        ' in a NASAuthList object (a glorified Dictionary, basically)
        secrets = New NASAuthList
        ' Populate from DB, I suppose ...
        secrets.AddSharedSecret("10.10.10.10", "router1")
        secrets.AddSharedSecret("10.10.10.20", "router2")
        secrets.AddSharedSecret("10.10.10.30", "firewall1")
        secrets.AddSharedSecret("10.10.10.40", "firewall2")

        ' Then, we just create a RADIUS server ...
        radius1812 = New RADIUSServer(1812, AddressOf ProcessPacket1812, secrets)
        ' If you need to listen on a second port at the same time,
        ' just create another server. Also, as a demo, let's listen
        ' on port 1645 on just one of the server IPs ...
        radius1645 = New RADIUSServer("10.1.1.1", 1645, AddressOf ProcessPacket1645, secrets)

        ' NOTE: If an error occurs (e.g. listening port not available, etc.),
        ' an exception will be thrown. Such exceptions are not handled explicitly
        ' in the RADIUS implementation, so they will bubble up to your code. It
        ' may be a good idea to wrap the above in a Try...Catch.

    End Sub


    ' Every valid RADIUS request generated by the server(s) we created earlier
    ' will fire up the callback procedure. Invalid requests are dropped, per RFC.
    Private Sub ProcessPacket1812(ByVal packet As RADIUSPacket)
        ProcessPacket(radius1812, packet)
    End Sub

    Private Sub ProcessPacket1645(ByVal packet As RADIUSPacket)
        ProcessPacket(radius1645, packet)
    End Sub

    Private Sub ProcessPacket(ByVal server As RADIUSServer, ByVal packet As RADAR.RADIUSPacket)

        ' Let's take a look at just authentication requests, 
        ' and drop other requests silently ...
        If packet.Code <> RadiusPacketCode.AccessRequest Then Exit Sub

        ' =============

        ' Let's see if we have a username present ...
        Dim username As RADIUSAttribute = packet.Attributes.GetFirstAttribute(RadiusAttributeType.UserName)

        ' If an attribute of a particular type is not found, the function
        ' will return Nothing.
        If username Is Nothing Then
            ' Technically, this case is against RFC, so ... drop.
            Exit Sub
        End If

        ' ===============

        MsgBox(username.GetString & " is trying to log in ... ")
        ' Note that an attribute can represent a string, number, IP, etc.
        ' RADAR will not guess that automatically, so use the appropriate
        ' function according to the attribute you're trying to read. Otherwise,
        ' the Value property is just a bunch of bytes as received in the 
        ' RADIUS packet.

        ' ===================

        ' Now, let's verify the password ...
        ' So, let's get valid Username/Password pairs loaded in an
        ' NASAuthList object ...
        Dim validCredentials As New NASAuthList
        ' You'd perhaps populate from DB ...
        validCredentials.AddSharedSecret("yadayada", "blahblah")
        validCredentials.AddSharedSecret("admin", "topsecretpassword")
        Dim success As Boolean = _
            packet.AuthenticateAccessRequest(validCredentials, secrets)

        ' Or ...
        success = (packet.UserPassword = "wow... a password!")

        Dim attributes As New RADIUSAttributes
        If success Then ' Yay! Someone guessed the password ...
            ' Let's reply back to the firewall / router that we're good ...
            ' This is the time to add any attributes we need to pass onto the 
            ' router / firewall ...
            ' For example ...
            If packet.EndPoint.Address.ToString = "10.10.10.10" Then
                ' If the request is coming from our favorite Cisco router ...
                If username.GetString = "careless.carl" Then
                    Dim ciscoParameter = New CiscoAVPair("shell:priv-lvl", "1")
                    ciscoParameter.GetRADIUSAttribute(attributes)
                    ' make sure careless Carl gets no exec privileges ...

                    ' This looks a bit backwards, but the GetRADIUSAttribute
                    ' function formats "ciscoParameter" and adds it to "attributes"

                    ' I should probably implement a more generic Vendor-Specific 
                    ' attribute (VSA) handling, rather than just Cisco VSAs.
                End If
            End If

            ' Compose and send a response ...
            ' We basically get all the information required from the 
            ' original request packet (identifier, end point, authenticator), 
            ' and throw in any additional attributes we want, as defined above ...
            server.SendAsResponse( _
                New RADIUSPacket(RadiusPacketCode.AccessAccept, _
                                 packet.Identifier, attributes, _
                                 packet.EndPoint), _
                packet.Authenticator)
        Else ' Wrong username / password ...
            server.SendAsResponse( _
                New RADIUSPacket(RadiusPacketCode.AccessReject, _
                                 packet.Identifier, attributes, _
                                 packet.EndPoint), _
                packet.Authenticator)
            ' FYI ... if no additional attributes need to be added
            ' to the response, you can sepcify Nothing instead of
            ' creating an empty RADIUSAttributes object.
        End If

    End Sub

End Module
